package tech

import (
	"slices"
	"ssp/set"
)

func (g *Grid) aicALSGetWeaks(node *ChainNode) []GroupItem {
	weaks := g.groupSeenGroups(node)
	if node.item.group[1] != nil {
		return weaks
	}
	if node.pre == nil || (node.pre != nil && node.pre.item.group != node.item.group) {
		for _, cand := range node.item.group[0].cands.GetList() {
			if cand == node.item.cand {
				continue
			}
			weak := GroupItem{cand, node.item.group}
			weaks = append(weaks, weak)
		}
	}
	cells := []*Cell{}
	for _, weak := range weaks {
		if weak.group[1] == nil && weak.group[0].cands.Count() <= 3 && weak.group != node.item.group {
			cells = append(cells, weak.group[0])
		}
	}
	//fmt.Println("!!!!", cells)
	for _, cell1 := range cells {
		if cell1.cands.Count() > 3 {
			continue
		}
		for _, cell2 := range cells {
			if cell1 == cell2 || cell2.cands.Count() > 3 {
				continue
			}
			//if cell2.cands.Count() != 3 || cell2.cands.Diff(cell1.cands).Count() != 1 ||
			if cell2.cands.Union(cell1.cands).Count() != 3 ||
				!isSameHouse([]*Cell{cell1, cell2}) {
				continue
			}
			weak := GroupItem{node.item.cand, [9]*Cell{cell1, cell2, nil}}
			weaks = append(weaks, weak)
		}
	}

	weaks = slices.Compact(weaks)
	//fmt.Println("@@@@", weaks)
	return weaks
}

func (g *Grid) aicALSGetStrongs(node *ChainNode) []GroupItem {
	strongs := g.groupedXCycleGetStrongs(node)
	//if node.item.group[1] == nil {
	//	return strongs
	//}
	if node.item.group[0].cands.Count() == 2 &&
		(node.pre == nil || (node.pre != nil && node.pre.item.group != node.item.group)) {
		for _, cand := range node.item.group[0].cands.GetList() {
			if cand == node.item.cand {
				continue
			}
			strong := GroupItem{cand, node.item.group}
			strongs = append(strongs, strong)
		}
	}

	//fmt.Println("@@@@", node.item)
	if ok, ss := getGroupALSSingle(node); ok {
		strongs = append(strongs, ss...)
	}
	if ok, ss := g.getGroupALSPairs(node); ok {
		strongs = append(strongs, ss...)
	}
	return strongs
}

func getGroupALSSingle(node *ChainNode) (ok bool, strongs []GroupItem) {
	group := node.item.group
	if group[1] == nil || group[2] != nil {
		return
	}
	allCands := unionCellCands(group[:2])
	if allCands.Count() != 3 {
		return
	}
	for _, cand := range allCands.GetList() {
		if cand == node.item.cand {
			continue
		}
		strong := GroupItem{}
		strong.cand = cand
		num := 0
		for _, cell := range group[:2] {
			if cell.cands.Has(cand) {
				strong.group[num] = cell
				num++
			}
		}
		strongs = append(strongs, strong)
	}
	ok = true
	return
}

func (g *Grid) getGroupALSPairs(node *ChainNode) (ok bool, strongs []GroupItem) {
	if node.item.group[1] != nil {
		return
	}
	first := node.item.group[0]
	if first.cands.Count() == 3 {
		c := node.item.cand
		ab := first.cands.Diff(set.NewSet(c))
		seconds := []*Cell{}
		//for _, cell := range append(g.rows[first.rowNum].cells[:], g.cols[first.colNum].cells[:]...) {
		for _, cell := range g.seenCells(first) {
			if cell.cands != ab {
				continue
			}
			seconds = append(seconds, cell)
		}
		seconds = removeRepetition(seconds, getCellsInPathFromGroup(node))
		if len(seconds) == 0 {
			return
		}
		for _, second := range seconds {
			for _, cand := range ab.GetList() {
				strong := GroupItem{cand, [9]*Cell{second, first, nil}}
				strongs = append(strongs, strong)
			}
		}
		ok = true
		return
	} else if first.cands.Count() == 2 {
		c := node.item.cand
		a := first.cands.Diff(set.NewSet(c))
		seconds := []*Cell{}
		//for _, cell := range append(g.rows[first.rowNum].cells[:], g.cols[first.colNum].cells[:]...) {
		for _, cell := range g.seenCells(first) {
			if cell.cands.Count() != 2 || cell.cands.Intersect(first.cands) != a {
				continue
			}
			seconds = append(seconds, cell)
		}
		seconds = removeRepetition(seconds, getCellsInPathFromGroup(node))
		if len(seconds) == 0 {
			return
		}
		for _, second := range seconds {
			//fmt.Println("first", first, "second", second)
			cand := second.cands.Diff(first.cands).GetList()[0]
			strong := GroupItem{cand, [9]*Cell{second, nil}}
			strongs = append(strongs, strong)
		}
		ok = true
		return
	} else {
		return
	}

}

func (s *Solve) AICWithALS3(g *Grid) (used bool, results []*Result) {
	c := Chain{
		g:            g,
		strategyName: "AIC With ALS",
		getWeaks:     g.aicALSGetWeaks,
		getStrongs:   g.aicALSGetStrongs,
		closeCycle:   oneOrTwoGroupCloseCycle,
		canRemove:    g.canRemoveAICGroup,
	}
	return c.ExecStrategy3()
}
